<?php

// classes in this file:
// BLL: Business Logic Layer
// 
// AchievementEvaluator: abstract class that checks if a given answer by a given user entitles
// the user to receive an achievement

class BLL {
	const SALT = "gnerognfeirmgor";

	// fixme temp hack because I have no time to write a loader w/ file format
	protected $achievementEvaluatorDefinitions = array(
		"The Tag Collector" => "TagCollectorAchievement"
		, "De Eerste Stap" => "EersteStapAchievement"
		//, "Too Easy" => "AlwaysAchievement"
		, "Winning Streak" => "WinningStreakAchievement"
		, "Netwerkspecialist" => "NetwerkspecialistAchievement"
		, "Private Investigator" => "PrivateInvestigatorAchievement"
		, "Historisch Besef" => "HistorischBesefAchievement"
	);
	protected $achievementsObtained = array();

	protected static $instance;
	protected $dml;

	protected function __construct() {
		$this->dml = DML::getInstance();
	}

	public static function getInstance() {
		if (empty(self::$instance)) {
			self::$instance = new self();
		}
		return self::$instance;
	}

	/**
	 * Magic __call method
	 *
	 * BLL aggregates DML and will try to pass a method call on to the DML (if
	 * the DML supports the method). Otherwise, a fatal error will be triggered.
	 */
	public function __call($name, $args) {
		return call_user_func_array(array($this->dml, $name), $args);
		/*if (method_exists($this->dml, $name)) {
			return call_user_func_array(array($this->dml, $name), $args);
		} else {
			user_error("Call to undefined method BLL::" . $name, E_USER_ERROR);
		}*/
	}

	/**
	 * Magic __get method
	 *
	 * BLL aggregates DML and will try to reference its members on retrieval.
	 * Setting members is explicitly not supported
	 */
	public function __get($name) {
		if (isset($this->dml->$name)) {
			return $this->dml->$name;
		}
		user_error("Undefined index:" . $name, E_USER_NOTICE);
		return NULL;
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
	// get currently active user
	// throws exception if there is no active user
	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
	public function getActiveUser() {
		if (empty($_SESSION["userID"])) {
			throw new Exception("No user active");
		}
		$result = $this->getUserByID($_SESSION["userID"]);
		if (empty($result)) {
			throw new Exception("Invalid user in session");
		}
		return $result;
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
	// determine if the current user has answered a certain question
	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
	public function currentUserHasAnswered(Question $question) {
		$me = $this->getActiveUser();
		foreach ($me->answersGiven as $answer) {
			if ($question->id == $answer->question->id) {
				return TRUE;
			}
		}
		return FALSE;
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-=-
	// send re-authentication link
	// -=-=-=-=-=-=-=-=-=-=-=-=-=-
	public function sendWelcome(User $user) {
		$msg = "";
		$msg .= sprintf("Beste %s,\n\n", $user->name);
		$msg .= sprintf("Welkom bij QrUIZ op de SURFnet Relatiedagen 2010!\n\n");
		$msg .= sprintf("Op dit moment is deze telefoon bekend bij QrUIZ. Mocht QrUIZ onverhoopt je uit het oog verliezen dan kun je met onderstaande link je telefoon opnieuw aanmelden.\n\n");
		$msg .= sprintf("%s", self::generateReauthenticationLink($user));
		$msg .= sprintf("\n\nVeel plezier!");

		$result = mail($user->email, "Welkom bij QrUIZ - Relatiedagen 2010", $msg, "From: noreply@qruiz.nl");
		// todo check where mail is going
		if (!$result) {
			throw new Exception("Aanmeldingsmail kon niet verstuurd worden");
		}
	}

	// -=-=-=-=-=-=-=-=-=-=-=
	// verify a question hash
	// -=-=-=-=-=-=-=-=-=-=-=
	public static function verifyQuestionHash($questionID, $hash) {
		return self::generateQuestionHash($questionID) == $hash;
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=
	// generate a question hash
	// -=-=-=-=-=-=-=-=-=-=-=-=
	public static function generateQuestionHash($questionID) {
		return sha1(self::SALT . $questionID);
	}

	// -=-=-=-=-=-=-=-=-=-=-
	// generate re-auth link
	// -=-=-=-=-=-=-=-=-=-=-
	public function generateReauthenticationLink(User $user) {
		$result = "http://";
		$result .= $_SERVER["SERVER_NAME"];
		$result .= "/";
		$result .= $_SERVER["PHP_SELF"];
		$result .= "?uid=" . $user->id;
		$result .= "&token=" . $this->generateUserIDHash($user->id);
		return $result;
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-=-
	// generate the hash for a uid
	// -=-=-=-=-=-=-=-=-=-=-=-=-=-
	public static function generateUserIDHash($userID) {
		return sha1(self::SALT . $userID);
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-
	// verify the hash for a uid
	// -=-=-=-=-=-=-=-=-=-=-=-=-
	public static function verifyUserIDHash($userID, $hash) {
		return $hash == self::generateUserIDHash($userID);
	}

	// -=-=-=-=-=-=-=-=
	// record an answer
	// -=-=-=-=-=-=-=-=
	public function giveAnswer(PossibleAnswer $answer) {
		// check for active user
		$user = $this->getActiveUser();
		if (empty($user)) {
			throw new Exception("U bent niet aangemeld");
		}
		// save user -> answer link in DML
		$this->dml->saveUserAnswer($user, $answer);

		foreach ($this->achievementEvaluatorDefinitions as $description => $className) {
			$evaluator = new $className($user);
			if ($evaluator->achievementObtained($answer)) {
				$achievement = $evaluator->getAchievement();
				$isOldAchievement = FALSE;
				foreach ($user->achievements as $alreadyObtainedAchievement) {
					if ($achievement->id == $alreadyObtainedAchievement->id) {
						$isOldAchievement = TRUE;
						break;
					}
				}
				// don't store achievements twice...
				if (!$isOldAchievement) {
					$this->saveUserAchievement($user, $achievement);
				}
				// ...but show them nevertheless
				$this->achievementsObtained[] = $achievement;
			}
		}
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	// return achievements obtained by the last answers
	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	public function getObtainedAchievements() {
		return $this->achievementsObtained;
	}
}

require_once("achievements.class.php");
